namespace SimpleWebServices.Web
{
    using System;
    using System.Collections.ObjectModel;
    using System.Net.Mime;
    using System.Web;
    using Moq;
    using SimpleWebServices;
    using SimpleWebServices.Web.Mvc;
    using Xunit;

    public sealed class AcceptFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<Accept>()
                .DerivesFrom<object>()
                .IsConcreteClass()
                .IsSealed()
                .HasDefaultConstructor()
                .Result);
        }

        [Fact]
        public void ctor()
        {
            Assert.NotNull(new Accept());
        }

        [Fact]
        public void prop_ContentTypes()
        {
            Assert.True(new PropertyExpectations(typeof(Accept).GetProperty("ContentTypes"))
                .TypeIs<Collection<ContentType>>()
                .DefaultValueIsNotNull()
                .Result);
        }

        [Fact]
        public void opImplicit_Accept_string()
        {
            string expected = "text/html";

            Accept obj = expected;

            Assert.Equal<int>(1, obj.ContentTypes.Count);
            Assert.Equal<string>(expected, obj.ContentTypes[0].MediaType);
        }

        [Fact]
        public void opImplicit_Accept_stringEmpty()
        {
            Accept obj = string.Empty;

            Assert.Equal<int>(1, obj.ContentTypes.Count);
            Assert.Equal<string>("*/*", obj.ContentTypes[0].MediaType);
        }

        [Fact]
        public void opImplicit_Accept_stringNull()
        {
            Accept obj = null as string;

            Assert.Equal<int>(1, obj.ContentTypes.Count);
            Assert.Equal<string>("*/*", obj.ContentTypes[0].MediaType);
        }

        [Fact]
        public void op_FromString_stringEmpty()
        {
            string expected = "*/*";
            string actual = Accept.FromString(string.Empty).ContentTypes[0].MediaType;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_FromString_stringNull()
        {
            string expected = "*/*";
            string actual = Accept.FromString(null as string).ContentTypes[0].MediaType;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_FromString_string()
        {
            string expected = "text/html";
            string actual = Accept.FromString(expected).ContentTypes[0].MediaType;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_FromString_stringSemicolon()
        {
            string expected = "text/html";
            string actual = Accept.FromString(expected + ";q=0.4").ContentTypes[0].MediaType;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_FromString_stringAccept()
        {
            string value = "text/*, text/html, text/html;level=1, */*";
            Accept obj = Accept.FromString(value);

            Assert.Equal<int>(3, obj.ContentTypes.Count);

            Assert.Equal<string>("text/html", obj.ContentTypes[0].MediaType);
            Assert.Equal<string>("text/*", obj.ContentTypes[1].MediaType);
            Assert.Equal<string>("*/*", obj.ContentTypes[2].MediaType);
        }

        [Fact]
        public void op_FromString_stringAcceptMisordered()
        {
            string value = "*/*, application/xhtml+xml, text/html;q=0.4";
            Accept obj = Accept.FromString(value);

            Assert.Equal<int>(3, obj.ContentTypes.Count);

            Assert.Equal<string>("application/xhtml+xml", obj.ContentTypes[0].MediaType);
            Assert.Equal<string>("text/html", obj.ContentTypes[1].MediaType);
            Assert.Equal<string>("*/*", obj.ContentTypes[2].MediaType);
        }

        [Fact]
        public void op_FromString_stringAcceptFirefox()
        {
            string value = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
            Accept obj = Accept.FromString(value);

            Assert.Equal<int>(4, obj.ContentTypes.Count);

            Assert.Equal<string>("text/html", obj.ContentTypes[0].MediaType);
            Assert.Equal<string>("application/xhtml+xml", obj.ContentTypes[1].MediaType);
            Assert.Equal<string>("application/xml", obj.ContentTypes[2].MediaType);
            Assert.Equal<string>("*/*", obj.ContentTypes[3].MediaType);
        }

        [Fact]
        public void op_FromString_stringWithSingleAsterixContentType()
        {
            Assert.Equal<int>(4, Accept.FromString("text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2").ContentTypes.Count);
        }

        [Fact]
        public void op_Negotiate_HttpRequestBase_Type()
        {
            Accept obj = Accept.FromString("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");

            var mock = new Mock<HttpRequestBase>();
            mock.SetupGet<string>(x => x.Path).Returns("/test").Verifiable();
            mock.SetupGet<string>(x => x.RawUrl).Returns("http://example.com/test").Verifiable();

            string expected = "/test.html";
            string actual = (obj.Negotiate(mock.Object, typeof(TestResourceController)) as SeeOtherResult).Location;

            Assert.Equal<string>(expected, actual);

            mock.VerifyAll();
        }

        [Fact]
        public void op_Negotiate_HttpRequestBaseNull_Type()
        {
            Assert.Throws<ArgumentNullException>(() => new Accept().Negotiate(null as HttpRequestBase, typeof(ResourceController)));
        }

        [Fact]
        public void op_Negotiate_HttpRequestBase_TypeNull()
        {
            Assert.Throws<ArgumentNullException>(() => new Accept().Negotiate(new Mock<HttpRequestBase>().Object, null as Type));
        }

        [Fact]
        public void op_Negotiate_HttpRequestBase_TypeInvalid()
        {
            Assert.Throws<ArgumentOutOfRangeException>(() => new Accept().Negotiate(new Mock<HttpRequestBase>().Object, typeof(string)));
        }
    }
}